﻿using Devonline.AspNetCore;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Net.Http.Headers;

namespace Devonline.Identity;

/// <summary>
/// identity HttpClient service
/// </summary>
public class IdentityService
{
    private readonly ILogger<IdentityService> _logger;
    private readonly AppSetting _appSetting;
    private readonly HttpClient _httpClient;
    private readonly HttpContext _httpContext;
    public IdentityService(
        ILogger<IdentityService> logger,
        AppSetting appSetting,
        HttpClient httpClient,
        IHttpContextAccessor httpContextAccessor
        )
    {
        ArgumentNullException.ThrowIfNull(httpContextAccessor.HttpContext);
        ArgumentNullException.ThrowIfNull(appSetting.Identity);
        _logger = logger;
        _appSetting = appSetting;
        _httpClient = httpClient;
        _httpContext = httpContextAccessor.HttpContext;
        _httpClient.BaseAddress = new Uri(_appSetting.Identity.Authority);
        _httpClient.DefaultRequestHeaders.Add(HeaderNames.Accept, ContentType.Json);
        _httpClient.DefaultRequestHeaders.Add(HeaderNames.UserAgent, _appSetting.Identity.ClientId);
    }

    /// <summary>
    /// 登录
    /// </summary>
    /// <returns></returns>
    public async Task<UserInfo> LoginAsync(UserLoginModel model)
    {
        ArgumentNullException.ThrowIfNull(model.UserName);
        _logger.LogDebug($"user {model.UserName} will login from Identity Service!");
        var token = await GetIdentityTokenAsync(model);
        ArgumentNullException.ThrowIfNull(token);
        _httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue(AppSettings.DEFAULT_AUTHENTICATION_SCHEME, token.AccessToken);
        var identityUserInfo = await GetIdentityUserInfoAsync(model.UserName);
        ArgumentNullException.ThrowIfNull(identityUserInfo);
        _logger.LogInformation($"user {model.UserName} login success and get the token and userinfo from Identity Service!");
        return new UserInfo { Token = token, User = identityUserInfo.CopyTo<UserViewModel>() };
    }
    /// <summary>
    /// 退出登录
    /// </summary>
    /// <returns></returns>
    public async Task LogoutAsync()
    {
        _logger.LogDebug("user will logout from Identity Service!");
        ArgumentNullException.ThrowIfNull(_appSetting.UserInteraction);
        var response = await _httpClient.GetAsync(_appSetting.UserInteraction.Logout);
        var result = response.IsSuccessStatusCode ? "success" : (await response.Content.ReadAsStringAsync());
        _logger.LogDebug($"user logout from Identity Service: {result}");
    }
    /// <summary>
    /// 获取用户信息
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    public async Task<UserInfo?> GetUserInfoAsync()
    {
        var userName = _httpContext.GetUserName();
        _logger.LogDebug($"user {userName} will get user info from Identity Service for current system");
        ArgumentNullException.ThrowIfNull(_appSetting.UserInteraction);

        string system = _httpContext.Request.GetRequestOrigin();
        using var request = new HttpRequestMessage(System.Net.Http.HttpMethod.Get, _appSetting.UserInteraction.UserInfo + AppSettings.CHAR_QUESTION + nameof(system) + AppSettings.CHAR_EQUAL + system);
        if (_httpContext.Request.Cookies.Any())
        {
            request.Headers.Add("Cookie", _httpContext.Request.Headers.Cookie.ToString());
        }

        if (_httpContext.Request.Headers.Authorization.Any())
        {
            request.Headers.Authorization = System.Net.Http.Headers.AuthenticationHeaderValue.Parse(_httpContext.Request.Headers.Authorization);
        }

        var response = await _httpClient.SendAsync(request);
        var result = await response.Content.ReadAsStringAsync();
        if (response.IsSuccessStatusCode)
        {
            var userInfo = result.ToJsonObject<UserInfo>();
            if (userInfo is not null && userInfo.User is not null)
            {
                _logger.LogInformation($"user {userName} get userinfo from Identity Service success");
                return userInfo;
            }
        }

        _logger.LogDebug($"user {userName} get userinfo from Identity Service failed: {result}");
        return default;
    }

    /// <summary>
    /// 获取 access token
    /// </summary>
    /// <param name="model"></param>
    /// <returns></returns>
    private async Task<IdentityToken?> GetIdentityTokenAsync(UserLoginModel model)
    {
        ArgumentNullException.ThrowIfNull(_appSetting.Identity);
        ArgumentNullException.ThrowIfNull(_appSetting.Identity.ClientId);
        ArgumentNullException.ThrowIfNull(_appSetting.Identity.ClientSecret);
        ArgumentNullException.ThrowIfNull(_appSetting.Identity.GrantType);
        _logger.LogDebug($"user {model.UserName} will get token from Identity Service!");
        var dic = new Dictionary<string, string>();
        var column = _appSetting.Identity.GetColumnName(nameof(_appSetting.Identity.ClientId));
        if (column is not null)
        {
            dic.Add(column, _appSetting.Identity.ClientId);
        }

        column = _appSetting.Identity.GetColumnName(nameof(_appSetting.Identity.ClientSecret));
        if (column is not null)
        {
            dic.Add(column, _appSetting.Identity.ClientSecret);
        }

        column = _appSetting.Identity.GetColumnName(nameof(_appSetting.Identity.GrantType));
        if (column is not null)
        {
            dic.Add(column, _appSetting.Identity.GrantType);
        }

        column = model.GetColumnName(nameof(model.UserName));
        if (column is not null && model.UserName is not null)
        {
            dic.Add(column, model.UserName);
        }

        column = model.GetColumnName(nameof(model.Password));
        if (column is not null && model.Password is not null)
        {
            dic.Add(column, model.Password);
        }

        var httpContent = new FormUrlEncodedContent(dic);
        httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType.FormUrlencoded);
        var response = await _httpClient.PostAsync(_appSetting.Identity.Token, httpContent);
        var result = await response.Content.ReadAsStringAsync();
        if (response.IsSuccessStatusCode)
        {
            var token = result.ToJsonObject<IdentityToken>();
            if (token is not null)
            {
                _logger.LogInformation($"user {model.UserName} get access token success: {result}");
                return token;
            }
        }

        _logger.LogDebug($"user {model.UserName} get token from Identity Service failed: {result}");
        return default;
    }
    /// <summary>
    /// get identity userinfo from identity service
    /// </summary>
    /// <param name="userName"></param>
    /// <returns></returns>
    private async Task<IdentityUserInfo?> GetIdentityUserInfoAsync(string userName)
    {
        ArgumentNullException.ThrowIfNull(_appSetting.Identity);
        _logger.LogDebug($"user {userName} will get userinfo from Identity Service!");
        var response = await _httpClient.GetAsync(_appSetting.Identity.UserInfo);
        var result = await response.Content.ReadAsStringAsync();
        if (response.IsSuccessStatusCode)
        {
            var userInfo = result.ToJsonObject<IdentityUserInfo>();
            if (userInfo is not null)
            {
                userInfo.PhoneNumber = userInfo.PhoneNumber.DesensitizePhoneNumber();
                _logger.LogInformation($"user {userName} get userinfo from Identity Service success: {result}");
                return userInfo;
            }
        }

        _logger.LogDebug($"user {userName} get userinfo from Identity Service failed: {result}");
        return default;
    }
    /// <summary>
    /// login to Identity Service
    /// </summary>
    /// <param name="model"></param>
    /// <returns></returns>
    private async Task<UserInfo?> IdentityLoginAsync(UserLoginModel model)
    {
        ArgumentNullException.ThrowIfNull(_appSetting.UserInteraction);
        _logger.LogDebug($"user {model.UserName} will login to Identity Service!");
        var httpContent = new StringContent(model.ToJsonString());
        httpContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(ContentType.Json);
        var response = await _httpClient.PostAsync(_appSetting.UserInteraction.Login, httpContent);
        var result = await response.Content.ReadAsStringAsync();
        if (response.IsSuccessStatusCode)
        {
            var userInfo = result.ToJsonObject<UserInfo>();
            if (userInfo is not null)
            {
                _logger.LogInformation($"user {model.UserName} login to Identity Service success: {result}");
                return userInfo;
            }
        }

        _logger.LogDebug($"user {model.UserName} login to Identity Service failed: {result}");
        return default;
    }
}